﻿using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using FluentParser;
using FluentParser.Expressions;
using FluentParser.Utils;

namespace FluentParser.Editor
{
    public class IniFileParserGrammar : Grammar<IniFile>
    {
        public IniFileParserGrammar()
        {
            // Defining error recovery strategy
            Symbol strategyMoveToNewLine = ParseSymbol.Regex("[^\r\n]*");
            Expr<string> strategyParseUntilBracket = ParseString.Regex("[^\r\n\\]]");

            // Defining list of errors
            Symbol errorMissingClosingBracket = Errors.SymbolError("Expected closing bracket", strategyMoveToNewLine);
            Expr<string> errorInvalidSectionName = Errors.Error("Invalid section name", strategyParseUntilBracket);
            Expr<string> errorInvalidSettingName = Errors.Error("Invalid setting name", strategyParseUntilBracket);
            Expr<Section> warningTooManySections = Errors.Warning<Section>("Too many sections");

            // Syntax tokens (valueless)
            Symbol spaces = ParseSymbol.Regex("[ \t]*");
            Symbol newLineOrDone = ParseSymbol.Regex("[\r\n]+") | EndOfText;
            Symbol anyWhitespace = ParseSymbol.Regex("[ \t\r\n]*");
            Symbol equality = Symbol("=").Surround(spaces);
            Symbol openSectionName = Symbol("[");
            Symbol closeSectionName = Symbol("]") | errorMissingClosingBracket;

            // Primitive value tokens
            Expr<string> parseName = ParseString.Regex("[A-Za-z0-9_-]+");
            Expr<string> parseValue = ParseString.Regex("[^\r\n]*");
            Expr<string> parseSectionName = parseName | errorInvalidSectionName;
            Expr<string> parseSettingName = parseName; // | errorInvalidSettingName;
            Expr<string> parseFullSectionName = openSectionName + spaces + parseSectionName + spaces + closeSectionName + spaces + newLineOrDone + anyWhitespace;

            // Complex value tokens
            Expr<Setting> parseSetting =
                (parseSettingName + equality + parseValue + newLineOrDone + anyWhitespace)
                    .Convert((name, value) => new Setting(name, value.Trim()));

            Expr<Section> parseSection =
                parseFullSectionName.Join(parseSetting.Repeat(0))
                    .Convert((name, settings) => new Section(name, settings));

            Expr<IniFile> fileExpr =
                parseSection.Repeat(0)
                    .Convert(sections => new IniFile(sections));

            // Defining colors
            parseFullSectionName.Color = Color.DarkBlue;
            parseSectionName.Color = Color.DarkRed;
            parseSettingName.Color = Color.DarkGreen;
            equality.Color = Color.DarkBlue;

            Root = anyWhitespace + fileExpr + EndOfText;
        }
    }

    public class ParseDictionaryGrammar : Grammar<Dictionary<string, string>>
    {
        public ParseDictionaryGrammar()
        {
            Symbol warningEndOfText = Errors.SymbolWarning("Unexpected end of text");

            // Terminals
            Symbol newLineOrDone = ParseSymbol.Regex("[\r\n]+") | (EndOfText & warningEndOfText);
            Expr<string> parseKey = ParseString.Regex("[A-Za-z0-9-_]+");
            Expr<string> parseValue = ParseString.Regex("[^\r\n]+");;
            Symbol equality = "=";

            // NonTerminals
            Expr<Tuple<string, string>> parseLine = parseKey + equality + parseValue + newLineOrDone;
            Expr<Tuple<string, string>[]> parseManyLines = parseLine.Repeat(0);
            Expr<Dictionary<string, string>> parseDictionary =
                parseManyLines.Convert(array => array.ToDictionary(x => x.Item1, x => x.Item2));

            Root = parseDictionary;
        }
    }

    public class IniFile
    {
        public Dictionary<string, Section> Sections { get; set; }

        public IniFile(IEnumerable<Section> sections)
        {
            Sections = sections.ToDictionary(x => x.Name);
        }
    }

    public class Section
    {
        public Section(string name, IEnumerable<Setting> values)
        {
            Name = name;
            Values = values.ToDictionary(x => x.Key, x => x.Value);
        }

        public string Name { get; private set; }
        public Dictionary<string, string> Values { get; private set; }
    }

    public class Setting
    {
        public Setting(string key, string value)
        {
            Key = key;
            Value = value;
        }

        public string Key { get; private set; }
        public string Value { get; private set; }
    }
}